package com.jar.exercise.多线程.线程回调;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 官方文档中说 DeferredResult 和 Callable 都是为了异步生成返回值提供基本的支持。
 * 简单来说就是一个请求进来，如果你使用了DeferredResult或者Callable，
 * 在没有得到返回数据之前，DispatcherServlet和所有Filter就会退出Servlet容器线程，
 * 但响应保持打开状态，一旦返回数据有了，这个DispatcherServlet就会被再次调用并且处理，
 * 以异步产生的方式，向请求端返回值。
 * 这么做的好处就是请求不会长时间占用服务连接池，提高服务器的吞吐量。
 * 1.比如消息中间件。 请求 与 响应的线程并不是同一个线程
 * 2.可以使用 DeferredResult 对象来解决
 * 需要注意的是，控制器（controller 的返回值是 DeferredResult 对象）
 * 应用1：此逻辑应用于服务端调用AI分析程序可以使用
 */
@RestController
@Slf4j
public class DeferredDemo {

    @GetMapping("/deferredResult")
    public DeferredResult<String> deferredResult() {
        DeferredResult<String> stringDeferredResult = new DeferredResult<>(5000L, "没有结果");
        CompletableFuture.supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(10);
                        return "最终结果";
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        return "错误结果";
                    }
                })
                .whenCompleteAsync((result, throwable) -> stringDeferredResult.setResult(result));

        stringDeferredResult.onTimeout(() -> {
            log.info("调用超时");
        });

        stringDeferredResult.onCompletion(() -> {
            log.info("调用完成");
        });
        return stringDeferredResult;
    }

    @GetMapping("/deferredResultUser")
    public DeferredResult<String> deferredResultListUser() {
        DeferredResult<String> deferredResult = new DeferredResult<>(5000L, new ResponseEntity<>(HttpStatus.NOT_MODIFIED));
        deferredResult.onTimeout(() -> {
            log.info("调用超时");
        });
        deferredResult.onCompletion(() -> {
            log.info("调用完成");
        });
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(10);
                deferredResult.setResult("result");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        return deferredResult;
    }

}
